package com.aoxiang.earlywaringapi.exceptionhandle;

import com.aoxiang.earlywaringapi.http.HttpException;
import com.aoxiang.earlywaringapi.http.ServerException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.List;
import java.util.StringJoiner;

/**
 * @author teevil@126.com
 * @date 2021/6/29
 */
@ControllerAdvice
public class GlobalExceptionHandle {
    /**
     * 处理未知异常:没有主动抛出的异常
     *
     * @param request HttpServletRequest 对象
     * @param e       Exception
     * @return UnifiedResponse
     */
    @ResponseBody
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ServerException handleException(HttpServletRequest request,
                                           Exception e) {
        e.printStackTrace();
        return new ServerException(
                9999,
                "服务器内部错误！"
        );
    }

    /**
     * 处理已知异常：主动抛出的异常
     *
     * @param request HttpServletRequest 对象
     * @param e       HttpException
     * @return UnifiedResponse
     */
    @ExceptionHandler(HttpException.class)
    public ResponseEntity<ServerException> handleHttpException(HttpServletRequest request,
                                                                       HttpException e) {
        ServerException unifiedResponse = new ServerException(
                e.getCode(),
                "Not know",
                this.getRequestPath(request)
        );
        HttpHeaders httpHeaders = new HttpHeaders();
        //设置Header
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpStatus httpStatus = HttpStatus.resolve(e.getHttpStatusCode());
        assert httpStatus != null;
        return new ResponseEntity<>(
                unifiedResponse,
                httpHeaders,
                httpStatus
        );
    }

    /**
     * httpBody参数校验不通过处理
     *
     * @param request HttpServletRequest
     * @param e       MethodArgumentNotValidException
     * @return UnifiedResponse
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ServerException handleBeanValidation(HttpServletRequest request,
                                                        MethodArgumentNotValidException e) {
        List<ObjectError> errors = e.getBindingResult().getAllErrors();
        String message = this.formatAllErrorsMessages(errors);
        return new ServerException(
                10001,
                message,
                this.getRequestPath(request)
        );
    }

    /**
     * 不传参数错误处理
     *
     * @param request HttpServletRequest
     * @param e       MissingServletRequestParameterException
     * @return UnifiedResponse
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ServerException handleMissingServletRequestParameterException(HttpServletRequest request,
                                                                                 MissingServletRequestParameterException e) {
        return new ServerException(
                10001,
                e.getParameterName() + "必须传递",
                this.getRequestPath(request)
        );
    }

    /**
     * 不支持调用方法错误处理，比如接口是post调用get
     *
     * @param request HttpServletRequest
     * @param e       HttpRequestMethodNotSupportedException
     * @return UnifiedResponse
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ServerException handleHttpRequestMethodNotSupportedException(HttpServletRequest request,
                                                                                HttpRequestMethodNotSupportedException e) {
        return new ServerException(
                10001,
                "当前发起请求的方法是[" + e.getMethod() + "],该接口只支持" + Arrays.toString(e.getSupportedMethods()) + "方法",
                this.getRequestPath(request)
        );
    }





    /**
     * post方法需要传递一个json对象什么都不传的异常处理
     *
     * @param request HttpServletRequest
     * @param e       HttpMessageNotReadableException
     * @return UnifiedResponse
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ServerException handleHttpMessageNotReadableException(HttpServletRequest request,
                                                                         HttpMessageNotReadableException e) {
        return new ServerException(
                10001,
                "请仔细检查请求参数，按照文档要求传递参数。",
                this.getRequestPath(request)
        );
    }


    /**
     * 把错误消息拼接起来。
     *
     * @param errors 错误消息List
     * @return errorMessages
     */
    private String formatAllErrorsMessages(List<ObjectError> errors) {
        StringJoiner errorMessages = new StringJoiner(";");
        errors.forEach(error ->
                errorMessages.add(error.getDefaultMessage())
        );
        return errorMessages.toString();
    }

    /**
     * @param request HttpServletRequest
     * @return String request.getMethod + request.getRequestURI
     */
    private String getRequestPath(HttpServletRequest request) {
        StringJoiner requestPath = new StringJoiner(" ");
        requestPath.add(request.getMethod());
        requestPath.add(request.getRequestURI());
        return requestPath.toString();
    }
}
